package com.xushi.controller;

import com.alibaba.fastjson.JSONObject;
import com.xushi.bean.AccessTokenResultBean;
import com.xushi.bean.ActivityGradeBean;
import com.xushi.model.Activity;
import com.xushi.model.ActivityGrade;
import com.xushi.model.WeixinUserInfo;
import com.xushi.service.ActivityGradeService;
import com.xushi.service.ActivityService;
import com.xushi.service.StorageService;
import com.xushi.service.WeixinUserInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.sql.Timestamp;
import java.util.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;



/**
 * grade表中username对应weinxin_user_info表中的openid
 */
@Controller
@RequestMapping("/littleGame")
public class LittleGameController {
    private static final String GET_ACCESS_TOKEN_URL_TEMPLATE =
            "https://api.weixin.qq.com/sns/oauth2/access_token?appid=wx3f546ee2bf5557b0&secret=0bca039b85091cfb562983d03dfd81e4&code=%s&grant_type=authorization_code";
    private static final String GET_ACCESS_TOKEN_URL_TEMPLATENEW =
            "https://api.weixin.qq.com/sns/jscode2session?appid=wx7d9ae9c3cd9a545d&secret=1176e4473e7df4443e52b0098697cc05&js_code=%s&grant_type=authorization_code";
    private static final String GET_USER_INFO_URL_TEMPLATE = "https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN";
    private static final String GET_ACCESS_TOKEN_TEMPLATE = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wx7d9ae9c3cd9a545d&secret=1176e4473e7df4443e52b0098697cc05";

    @Autowired
    private ActivityGradeService activityGradeService;
    @Autowired
    private WeixinUserInfoService weixinUserInfoService;
    @Autowired
    private ActivityService activityService;
    @Autowired
    private StorageService storageService;

    @RequestMapping("/uploadGrade")
    @ResponseBody
    public HashMap<String, Object> uploadGrade(@RequestParam("grade") int grade, @RequestParam String activityId,
                                               @RequestParam String username, HttpServletResponse response) {
        response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
        System.out.println("uploadGrade: " + username);
        HashMap<String, Object> result = new HashMap<>();
        if (StringUtils.isEmpty(activityId) || StringUtils.isEmpty(username) || username.equals("undefined")) {
            result.put("code", 2);
            result.put("message", "activity或username为空");
            return result;
        }
        Timestamp currTime = new Timestamp(System.currentTimeMillis());
        try {
            activityGradeService.saveGrade(grade, activityId, username, currTime);
        } catch (Exception e) {
            result.put("code", 2);
            result.put("message", e.toString());
            return result;
        }
        result.put("code", 1);
        return result;
    }

    @GetMapping("/getGrade")
    @ResponseBody
    public HashMap<String, Object> getGrade(@RequestParam String activityId,
                                            @RequestParam String username) {
        HashMap<String, Object> map = new HashMap<>();
        if (StringUtils.isEmpty(activityId) || StringUtils.isEmpty(username)) {
            map.put("code", "2");
            map.put("error", "Input can not be empty");
            return map;
        }
        try {
            List<ActivityGrade> activityGrades = activityGradeService.getGrade(activityId, username); //TODO：显示本次成绩
            if (CollectionUtils.isEmpty(activityGrades)) {
                map.put("code", "3");
                map.put("error", "The result of the query is empty");
                return map;
            }
            ActivityGrade activityGrade = activityGrades.get(0);
            for (int i = 0; i < activityGrades.size(); i++) {
                ActivityGrade temp = activityGrades.get(i);
                if (temp.getTime().after(activityGrade.getTime())) {    //找出最高成绩而非最新成绩
                    activityGrade = temp;
                }
            }
            WeixinUserInfo weixinUserInfo = weixinUserInfoService.getWeixinUserInfoByOpenId(username);
            String userInfo = "";
            if (!ObjectUtils.isEmpty(weixinUserInfo)) {
                userInfo = weixinUserInfo.getUserInfo();
            }
            JSONObject userInfoJson = JSONObject.parseObject(userInfo);
            ActivityGradeBean activityGradeBean = new ActivityGradeBean(activityGrade, userInfoJson);
            map.put("code", "1");
            map.put("activityGrade", activityGradeBean);
        } catch (Exception e) {
            map.put("code", "4");
            map.put("error", "Gain failure");
            map.put("msg", e.toString());
        }
        return map;
    }

    /*
        获取指定小游戏的所有玩家的成绩
        该接口已被getGradeListRanking替换，现作为子功能使用
    */
    @GetMapping("/getGradeList")
    @ResponseBody
    public HashMap<String, Object> getGrade(@RequestParam String activityId) {
        HashMap<String, Object> map = new HashMap<>();
        if (StringUtils.isEmpty(activityId)) {
            map.put("code", "2");
            map.put("error", "Input can not be empty");
            return map;
        }

        try {
            List<ActivityGrade> activityGrades = activityGradeService.getGradeList(activityId);
            if (CollectionUtils.isEmpty(activityGrades)) {
                map.put("code", "3");
                map.put("error", "The result of the query is empty");
                return map;
            }
            HashMap<String, ActivityGrade> tempMap = new HashMap<>();
            for (int i = 0; i < activityGrades.size(); i++) {
                ActivityGrade temp = activityGrades.get(i);
                tempMap.putIfAbsent(temp.getUsername(), temp);
                if (temp.getGrade() > tempMap.get(temp.getUsername()).getGrade())    //找出最高成绩而非最新成绩
                    tempMap.put(temp.getUsername(), temp);
            }

            List<String> openIdList = new ArrayList<>(tempMap.keySet());

            List<WeixinUserInfo> weixinUserInfos = weixinUserInfoService.findWeixinUserInfosByOpenIdIn(openIdList);

            HashMap<String, WeixinUserInfo> userInfoMap = new HashMap<>();
            for (WeixinUserInfo weixinUserInfo : weixinUserInfos) {
                userInfoMap.put(weixinUserInfo.getOpenId(), weixinUserInfo);
            }

            List<ActivityGradeBean> activityGradeBeanList = new ArrayList<>(openIdList.size());

            for (Map.Entry<String, ActivityGrade> entry : tempMap.entrySet()) {
                WeixinUserInfo weixinUserInfo = userInfoMap.get(entry.getKey());
                ActivityGrade activityGrade = entry.getValue();
                String userInfo = "";
                if (!ObjectUtils.isEmpty(weixinUserInfo)) {
                    userInfo = weixinUserInfo.getUserInfo();
                }
                JSONObject userInfoJson = JSONObject.parseObject(userInfo);
                activityGradeBeanList.add(new ActivityGradeBean(activityGrade, userInfoJson));
            }

            map.put("code", "1");
            map.put("activityGradeList", activityGradeBeanList);
        } catch (Exception e) {
            map.put("code", "4");
            map.put("error", "failure");
            map.put("msg", e.toString());
        }
        return map;
    }

    @PostMapping("/getGradeRanking")
    @ResponseBody
    public HashMap<String, Object> getGradeRanking(@RequestParam String activityId, @RequestParam String username) {
        HashMap<String, Object> map = new HashMap<>();
        List<ActivityGradeBean> res = new ArrayList<>();
        List<ActivityGradeBean> activityGradeBeanList = null;
        try {
            map = getGrade(activityId);
            activityGradeBeanList = (ArrayList<ActivityGradeBean>) (map.get("activityGradeList"));
            if (activityGradeBeanList == null)
                return map;
        } catch (Exception e) {
            map.put("code", "5");
            map.put("error", "failure");
            map.put("msg", e.toString());
            return map;
        }
        Comparator c = new Comparator<ActivityGradeBean>() {
            @Override
            public int compare(ActivityGradeBean o1, ActivityGradeBean o2) {
                // TODO Auto-generated method stub
                if (o1.getGrade() < o2.getGrade())
                    return 1;
                else return -1;
            }
        };
        try {
            Collections.sort(activityGradeBeanList, c);
        } catch (Exception e) {
            map.clear();
            map.put("code", "6");
            map.put("error", "failure");
            map.put("msg", e.toString());
            return map;
        }
        int index = -1;
        for (int i = 0; i < activityGradeBeanList.size(); i++) {
            if (activityGradeBeanList.get(i).getUsername().equals(username)) {
                index = i;
                break;
            }
        }
        if (index == -1) {
            map.clear();
            map.put("code", "7");
            map.put("error", "failure");
            return map;
        } else if (activityGradeBeanList.size() < 9) {
            for (int i = 0; i < activityGradeBeanList.size(); i++)
                res.add(activityGradeBeanList.get(i));
        } else if (index == 0 || index == 1 || index == 2 || index == 3) {
            for (int i = 0; i < 9; i++)
                res.add(activityGradeBeanList.get(i));
        } else {
            res.add(activityGradeBeanList.get(0));
            res.add(activityGradeBeanList.get(1));
            res.add(activityGradeBeanList.get(2));
            res.add(activityGradeBeanList.get(index - 1));
            res.add(activityGradeBeanList.get(index));
            for (int i = 1; i <= 4 && index + i < activityGradeBeanList.size(); i++) {
                res.add(activityGradeBeanList.get(index + i));
            }
        }
        map.put("activityGradeList", res);
        map.put("number", index + 1);
        System.out.println("code: "+ map.get(5));
        return map;
    }

    @PostMapping("/uploadUserInfo")
    @ResponseBody
    public HashMap<String, Object> uploadUserInfo(@RequestParam String openid, @RequestParam String nickname,
                                                  @RequestParam String userInfo) {

        HashMap<String, Object> map = new HashMap<>();
        Timestamp updateAt = new Timestamp(System.currentTimeMillis());
        Timestamp createAt = updateAt;


        try {
            WeixinUserInfo temp = weixinUserInfoService.getWeixinUserInfoByOpenId(openid);
            if (temp != null) {
                createAt = temp.getCreateAt();
                weixinUserInfoService.deleteByOpenId(openid);
            }
            nickname = filterEmoji(nickname);
            userInfo = filterEmoji(userInfo);
            weixinUserInfoService.addWeixinUserInfo(openid, nickname, userInfo, createAt, updateAt);
        } catch (Exception e) {
            map.put("code", 2);
            map.put("message", e.toString());
            return map;
        }

        map.put("code", 1);
        map.put("message", "success");
        return map;
    }

    @GetMapping("/getWeixinQrCode")
    @ResponseBody
    public ResponseEntity<Object> getWeixinQrCode(@RequestParam int activityId) {
        Activity activity = activityService.getActivityById(activityId);

        if (ObjectUtils.isEmpty(activity)) {
            return ResponseEntity.ok().body("activity not found");
        }

        try {
            Resource file = storageService.loadAsResource(activity.getUserTemplate().getWeixinQrCodePath());
            return ResponseEntity.ok().header(HttpHeaders.CONTENT_TYPE,
                    "image/png").body(file);
        } catch (Exception e) {
            return ResponseEntity.ok().body("file not found");
        }

    }

    @GetMapping("/getOpenId")
    @ResponseBody
    public HashMap<String, Object> getOpenId(@RequestParam String code) throws UnsupportedEncodingException {
        RestTemplate restTemplate = new RestTemplate();
        String GET_ACCESS_TOKEN_URL = String.format(GET_ACCESS_TOKEN_URL_TEMPLATE, code);
        HashMap<String, Object> result = new HashMap<>();
        ResponseEntity<String> responseEntity =
                restTemplate.getForEntity(GET_ACCESS_TOKEN_URL, String.class);
        JSONObject jsonObject = JSONObject.parseObject(responseEntity.getBody());
        if (jsonObject.containsKey("errcode")) {
            result.put("code", 2);
            result.put("message", "invalid code");
            result.put("origin", jsonObject);
            return result;
        }
        AccessTokenResultBean accessTokenResultBean = jsonObject.toJavaObject(AccessTokenResultBean.class);
        String openId = accessTokenResultBean.getOpenid();
        String GET_USER_INFO_URL = String.format(GET_USER_INFO_URL_TEMPLATE, accessTokenResultBean.getAccess_token(), openId);

        ResponseEntity<String> userInfoEntity =
                restTemplate.getForEntity(GET_USER_INFO_URL, String.class);

        String str = new String(userInfoEntity.getBody().getBytes("ISO-8859-1"), "UTF-8");
        str = filterEmoji(str);
        JSONObject userInfo = JSONObject.parseObject(str);
        String nickname = userInfo.getString("nickname");
        nickname = filterEmoji(nickname);

        System.out.println("userInfo: " + userInfo.toJSONString());
        System.out.println("accessToken: " + accessTokenResultBean.getAccess_token());
        System.out.println("refreshToken: " + accessTokenResultBean.getRefresh_token());
        System.out.println("getUserInfo url: " + GET_USER_INFO_URL);

        Timestamp updateAt = new Timestamp(System.currentTimeMillis());
        Timestamp createAt = updateAt;
        weixinUserInfoService.saveWeixinUserInfo(openId, nickname, userInfo.toJSONString(), createAt, updateAt);

        result.put("code", 1);
        result.put("openId", openId);
        return result;
    }

    @GetMapping("/getOpenIdNew")
    @ResponseBody
    public HashMap<String, Object> getOpenIdNew(@RequestParam String code, @RequestParam String userinfo) throws UnsupportedEncodingException {
        RestTemplate restTemplate = new RestTemplate();
        String GET_ACCESS_TOKEN_URL = String.format(GET_ACCESS_TOKEN_URL_TEMPLATENEW, code);
        String GET_ACCESS_TOKEN = String.format(GET_ACCESS_TOKEN_TEMPLATE);
        HashMap<String, Object> result = new HashMap<>();
        ResponseEntity<String> responseEntity =
                restTemplate.getForEntity(GET_ACCESS_TOKEN_URL, String.class);
        JSONObject jsonObject = JSONObject.parseObject(responseEntity.getBody());
        if (jsonObject.containsKey("errcode")) {
            result.put("code", 2);
            result.put("message", "invalid code1");
            result.put("origin", jsonObject);
            return result;
        }
        ResponseEntity<String> responseEntity1 =
                restTemplate.getForEntity(GET_ACCESS_TOKEN, String.class);
        JSONObject jsonObject1 = JSONObject.parseObject(responseEntity1.getBody());
        if (jsonObject1.containsKey("errcode")) {
            result.put("code", 2);
            result.put("message", "invalid code2");
            result.put("origin", jsonObject);
            return result;
        }
        AccessTokenResultBean accessTokenResultBean = jsonObject.toJavaObject(AccessTokenResultBean.class);
        String openId = accessTokenResultBean.getOpenid();
        String token = jsonObject1.getString("access_token");
//        String GET_USER_INFO_URL = String.format(GET_USER_INFO_URL_TEMPLATE, token, openId);

//        ResponseEntity<String> userInfoEntity =
//                restTemplate.getForEntity(GET_USER_INFO_URL, String.class);

//        String str = new String(userInfoEntity.getBody().getBytes("ISO-8859-1"), "UTF-8");
        userinfo = filterEmoji(userinfo);
        JSONObject userInfo = JSONObject.parseObject(userinfo);
        String nickname = userInfo.getString("nickName");
        nickname = filterEmoji(nickname);

        System.out.println("userInfo: " + userInfo.toJSONString());
        System.out.println("accessToken: " + token);

        Timestamp updateAt = new Timestamp(System.currentTimeMillis());
        Timestamp createAt = updateAt;
        weixinUserInfoService.saveWeixinUserInfo(openId, nickname, userInfo.toJSONString(), createAt, updateAt);

        result.put("code", 1);
        result.put("openId", openId);
        result.put("token: ", token);
        return result;
    }

    //替换emoji
    public static String filterEmoji(String source) {
        if (source != null && source.length() > 0) {
            return source.replaceAll("[\ud800\udc00-\udbff\udfff\ud800-\udfff]", "");
        } else {
            return source;
        }
    }

    @GetMapping("/sign")
    @ResponseBody
    public static Map<String, String> sign(@RequestParam String jsapi_ticket, @RequestParam  String url) {
            Map<String, String> ret = new HashMap<String, String>();
            String nonce_str = create_nonce_str();
            String timestamp = create_timestamp();
            String string1;
            String signature = "";
            String appId  = "wx3f546ee2bf5557b0";
            // 注意这里参数名必须全部小写，且必须有序
            string1 = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + nonce_str
                    + "&timestamp=" + timestamp + "&url=" + url;
            System.out.println(string1);
            try {
                MessageDigest crypt = MessageDigest.getInstance("SHA-1");
                crypt.reset();
                crypt.update(string1.getBytes("UTF-8"));
                signature = byteToHex(crypt.digest());
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            ret.put("url", url);
            //注意这里 要加上自己的appId
            ret.put("appId", appId);
            ret.put("jsapi_ticket", jsapi_ticket);
            ret.put("nonceStr", nonce_str);
            ret.put("timestamp", timestamp);
            ret.put("signature", signature);

            return ret;
        }

        private static String byteToHex(final byte[] hash) {
            Formatter formatter = new Formatter();
            for (byte b : hash)
            {
                formatter.format("%02x", b);
            }
            String result = formatter.toString();
            formatter.close();
            return result;
        }
        private static String create_nonce_str() {
            return UUID.randomUUID().toString();
        }
        private static String create_timestamp() {
            return Long.toString(System.currentTimeMillis() / 1000);
        }
}
